"""

题目描述：设计一个循环队列，支持如下操作：
MyCircularQueue (k) 构造器，设置队列最大长度为 k
Front () 从队首获取元素。如果队列为空，返回 -1
Rear () 获取队尾元素。如果队列为空，返回 -1
enQueue (value) 向循环队列插入一个元素。如果成功插入则返回真
deQueue () 从循环队列中删除一个元素。如果成功删除则返回真
isEmpty () 检查循环队列是否为空
isFull () 检查循环队列是否为满。

个人补充：
循环队列的核心特性本质是为解决 “普通队列假溢出” 问题而设计，结合其结构和操作逻辑，关键特性可归纳为以下 4 点，每点均对应其核心设计目标：
特性 1：空间循环利用，解决 “假溢出”
普通队列（数组实现）中，当队尾指针达到数组末尾时，即使数组前端仍有空闲空间，也无法继续入队（称为 “假溢出”）。循环队列通过取模运算让队尾、队首指针 “绕回” 数组前端，实现空间循环使用。例如：
队尾指针 rear 移动时，计算公式为 rear = (rear + 1) % capacity（capacity 是队列总容量）；
当 rear 到达数组最后一个索引（如 capacity-1）时，下一次入队会通过取模回到索引 0，复用前端空闲空间。

特性 2：队空与队满需额外标记(判断)，无法仅靠指针判断
普通队列中，“队空” 是 front == rear，“队满” 是 rear == capacity-1；但循环队列中，front == rear 既可能是队空，也可能是队满（例如：队列刚好装满时，rear 绕回后与 front 重合）。因此必须通过额外方式区分：
    方式 1：用一个 size 变量记录当前元素个数，size == 0 为队空，size == capacity 为队满；
    方式 2：预留一个数组位置不存储元素，队满条件变为 (rear + 1) % capacity == front，队空仍为 front == rear。

特性 3：先进先出（FIFO），操作逻辑与普通队列一致
循环队列本质仍是 “队列”，需严格遵循 “先进先出” 的核心规则：
入队（enQueue）：仅从队尾插入元素，插入后 rear 按循环规则移动；
出队（deQueue）：仅从队首删除元素，删除后 front 按循环规则移动；
访问队首 / 队尾元素：仅能通过 front 指针访问队首，通过 rear 指针访问队尾，无法随机访问中间元素。

特性 4：固定容量，需初始化时指定最大长度
循环队列通常基于固定大小的数组实现（动态扩容会破坏 “循环” 逻辑），因此必须在初始化时明确队列的最大容量 capacity，后续无法动态调整（若需扩容，需重新创建更大容量的新循环队列并迁移元素）。
这一特性决定了循环队列适合 “已知元素最大数量” 的场景（如缓冲区设计、固定大小的任务队列），避免普通动态队列的内存波动。

以上 4 点特性覆盖了循环队列的 “设计初衷”“核心约束” 和 “操作规则”，也是其与普通队列最关键的区别，在算法题（如设计循环队列）或实际应用（如操作系统缓冲区）中均为高频考点。

"""

class MyCircularQueue:
    def __init__(self, k: int):
        """初始化循环队列，容量为k"""
        self.capacity = k  # 队列最大容量
        #提前分配固定大小的存储空间（容量为 k），符合循环队列 “容量固定” 的特性。为后续的入队、出队等操作提供了基础存储空间。
        #初始值 0 只是占位符，后续会被实际入队的元素覆盖；
        self.queue = [0]*k  # 存储元素的数组 一开始都给默认赋上0
        self.front = 0  # 队首指针，指向第一个元素
        #队列刚初始化时，没有任何元素。若 rear 初始为 -1，意味着 “队尾指针还未指向任何有效的数组索引”（因为数组索引从 0 开始），能非常直观地体现 “队列当前没有元素” 这一状态。
        #且可以配合入队操作的逻辑(关于rear)
        self.rear = -1  # 队尾指针，指向最后一个元素
        self.size = 0  # 当前元素实际的数量

    def enQueue(self, value: int) -> bool:
        """向队列插入元素"""
        if self.isFull():
            return False  # 队列满则插入失败
        # 计算新的队尾位置（循环特性） 先计算要插入数据的位置 再正式插入
        self.rear = (self.rear + 1) % self.capacity
        self.queue[self.rear] = value  # 存入元素
        self.size += 1  # 元素数量加1
        return True

    def deQueue(self) -> bool:
        """从队列删除元素"""
        if self.isEmpty():
            return False  # 队列空则删除失败
        # 移动队首指针（循环特性）  front指针后移了一位，就算是删除了队列首部元素，因为之前那个位置不会再被用到了
        #这里并没有显式地对队列底层存储数据的结构（比如数组）进行元素的删除操作，而是通过 “移动队首指针 + 逻辑上标记元素数量减少” 的方式，
        # 间接实现了 “删除队首元素” 的效果，核心逻辑基于循环队列的 “逻辑复用” 特性
        #循环队列通常用固定大小的数组存储元素，并通过 front（队首指针）和 rear（队尾指针）标记队列的范围。由于数组大小固定且要循环利用空间，所以它不通过 “物理删除数组元素”（比如把后面元素前移）来实现出队，而是通过移动指针改变 “有效元素的范围”，配合 size（当前元素数量）来标记哪些元素是 “逻辑上有效的”。
        #因为循环队列的核心是空间循环复用，底层数组的元素会被反复覆盖。比如，当队首指针移走后，后续入队的元素会在 “下一次循环到该位置时” 直接覆盖旧值，所以不需要显式删除旧元素，只需要通过指针和 size 维护 “哪些位置的元素是当前队列有效的” 即可。
        #简单来说：循环队列的 “删除” 是逻辑层面的 “范围裁剪”，而非物理层面的 “数据清除”—— 通过移动 front 指针跳过旧队首，并减少 size 标记，就达到了 “队首元素不再有效” 的效果。
        self.front = (self.front + 1) % self.capacity
        self.size -= 1  # 元素数量减1
        return True

    def Front(self) -> int:
        """获取(查看)队首元素"""
        if self.isEmpty():
            return -1
        return self.queue[self.front]

    def Rear(self) -> int:
        """获取(查看)队尾元素"""
        if self.isEmpty():
            return -1
        return self.queue[self.rear]

    def isEmpty(self) -> bool:
        """判断队列是否为空"""
        return self.size == 0

    def isFull(self) -> bool:
        """判断队列是否为满"""
        return self.size == self.capacity

# 测试示例
obj = MyCircularQueue(3)
print(obj.enQueue(1))  # True
print(obj.enQueue(2))  # True
print(obj.enQueue(3))  # True
print(obj.enQueue(4))  # False（队列满） 测试队列满时，插入能否成功
print(obj.Rear())      # 3
print(obj.isFull())    # True
print(obj.deQueue())   # True
print(obj.enQueue(4))  # True 已经出队一个元素了 这里再入队一个 刚好队列满了
print(obj.Rear())      # 4